/*
 * Eugen Stoian <stoian.e@gmail.com> 2010
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#ifndef SMARTPTR_H
#define SMARTPTR_H
#include <Traits.h>
#include <typeinfo>
/**

  */
template<typename T>
class SmartPtr;

template<typename T>
class SmartPtr<const T>{
public:
    /** \fn SmartPtr(const T* ptr = 0, int* counter)
      * \brief Constructor
      * \param ptr Pointer to newly constructed object ( the pointee)
      * \param counter Pointer to reference counter
      */
    SmartPtr(const T* ptr = 0, int* counter = 0);

    /** \fn SmartPtr(const SmartPtr& rhs)
      * \brief Copy constructor
      * \param rhs Const reference to a smart pointer of the same type
      *
      */
    SmartPtr(const SmartPtr& rhs);

    /** \fn virtual ~SmartPtr()
      * \brief Destructor
      *
      *
      */
    virtual ~SmartPtr();

    /** \fn SmartPtr& operator=(const SmartPtr& rhs)
      * \brief Assignment operator
      * \param rhs Const reference to a smart pointer of the same type
      *
      */
    SmartPtr& operator=(const SmartPtr& rhs);

    /** \fn const T* operator->() const
      * \brief Pointer to member operator
      *
      *
      */
    const T* operator->() const;

    /** \fn const T& operator*() const
      * \brief Indirection(dereference) operator
      *
      *
      */
    const T& operator*() const;


    /** \fn template <typename U> operator SmartPtr<const U> const
      * \brief Cast operator to a smart pointer of different type
      * The cast is performed by dynamic casting the pointee.
      * If the cast fails the result is a smart pointer to null;
      * \throws If macro CSMARTPTR_CAST_THROWS is defined method may thow std::bad_cast on unsuccessful cast
      */
    template <typename U> operator SmartPtr<const U>() const
#ifdef CSMARTPTR_CAST_THROWS
    throw(std::bad_cast)
#endif
    ;

    /** \fn bool isNull() const
      * \brief Return true if the pointer is null, false otherwise
      *
      *
      */
    bool isNull() const;
    /** \fn bool isValid() const

      * \brief Return true if the pointer is not null, false otherwise
      *
      *
      */
    inline bool isValid() const { return !isNull(); }

    /** \fn bool isShared() const
      * \brief Returns true if the pointee is referenced by more than one smart pointer, false otherwise
      *
      *
      */
    bool isShared() const;

    /** \fn operator bool() const
      * \brief Cast operator to bool
      * Returns false if pointer is null, true otherwise
      *
      */
    operator bool() const {
        return _refCounter;
    }

    /** \fn bool operator==(const SmartPtr& rhs) const
      * \brief Equality test operator
      * \param rhs A smart pointer of the same type
      * Returns true if this pointer and rhs share the same pointee, false otherwise
      */
    bool operator==(const SmartPtr& p) const;

    /** \fn bool operator!=(const SmartPtr& p) const
      * \brief Non-equality test operator
      *
      *
      */
    bool operator!=(const SmartPtr& p) const;

    /** \fn void detach()
      * \brief Detaches the current smart pointer.
      * This method is useful for using in COW cases.
      * If current reference counter is less or equal with 1 does nothing.
      * Otherwise reference counter is decremented, a deep copy of pointee
      * is performed and a new reference counter is used and initialized with 1.
      *
      */
    void detach();

    friend class SmartPtr<T>;

protected:
    /** \fn void retain() const
      * \brief Increments reference counter with 1 (if not null)
      *
      *
      */
    void retain() const;

    /** \fn void release() const
      * \brief Decrements reference counter with 1 (if not null)
      * If reference counter becomes 0 then both the pointee and the reference
      * counter are destroyed and reference counter becomes null.
      */
    void release() const;
private:
    const T * _ptr;

    mutable int * _refCounter;
};


template<typename T>
class SmartPtr{
public:

    /** \fn SmartPtr(const T* ptr = 0, int* counter = 0)
      *
      *
      *
      */
    SmartPtr(const T* ptr = 0, int* counter = 0);


    /** \fn SmartPtr(const SmartPtr& rhs)
      *
      *
      *
      */
    SmartPtr(const SmartPtr& rhs);


    /** \fn ~SmartPtr()
      *
      *
      *
      */
    virtual ~SmartPtr();


    /** \fn SmartPtr& operator=(const SmartPtr& rhs)
      *
      *
      *
      */
    SmartPtr& operator=(const SmartPtr& rhs);

    /** \fn T* operator->() const
      *
      *
      *
      */
    T* operator->() const;

    /** \fn T& operator*() const
      *
      *
      *
      */
    T& operator*() const;


    /** \fn template <typename U> operator SmartPtr<const U> const
      *
      *
      *
      */
    template <typename U> operator SmartPtr<U>() const
#ifdef CSMARTPTR_CAST_THROWS
    throw(std::bad_cast)
#endif
    ;

    /** \fn bool isNull() const
      *
      *
      *
      */
    bool isNull() const;

    /** \fn bool isShared() const
      *
      *
      *
      */
    bool isShared() const;
    /** \fn bool isValid() const
      *
      *
      *
      */
    inline bool isValid() const{
        return !isNull();
    }
    /** \fn operator bool() const
      *
      *
      *
      */
    operator bool() const {
        return _refCounter;
    }

    /** \fn bool operator==(const SmartPtr& p) const
      *
      *
      *
      */
    bool operator==(const SmartPtr& p) const;

    /** \fn bool operator!=(const SmartPtr& p) const
      *
      *
      *
      */
    bool operator!=(const SmartPtr& p) const;

    /** \fn void detach()
      *
      *
      *
      */
    void detach();

    friend class SmartPtr<const T>;

protected:

    /** \fn
      *
      *
      *
      */
    void retain() const;

    /** \fn
      *
      *
      *
      */
    void release() const;

    T * _ptr;

    mutable int * _refCounter;
};

/*
    IMPLEMENTARE SmartPtr<const T>
*/
template <typename T>
SmartPtr<const T>::SmartPtr(const T* ptr, int* counter):_ptr(ptr),_refCounter(counter){
    if(_ptr){
        if(!_refCounter)
            _refCounter = new int(0);
        retain();
    }
    else {
        _refCounter = 0;
    }
}

template <typename T>
SmartPtr<const T>::SmartPtr(const SmartPtr& rhs):_ptr(rhs._ptr),_refCounter(rhs._refCounter){
    retain();
}

template <typename T>
SmartPtr<const T>::~SmartPtr(){
    release();
}

template <typename T>
SmartPtr<const T>& SmartPtr<const T>::operator=(const SmartPtr& rhs){
    rhs.retain();
    release();
    _refCounter = rhs._refCounter;
    _ptr = rhs._ptr;
    return *this;
}


template <typename T>
const T* SmartPtr<const T>::operator->() const{
    return _ptr;
}

template <typename T>
const T& SmartPtr<const T>::operator*() const{
    return *_ptr;
}

template <typename T>
template <typename U>
SmartPtr<const T>::operator SmartPtr<const U> () const
#ifdef CSMARTPTR_CAST_THROWS
throw(std::bad_cast)
#endif
{
    const U* ptr = dynamic_cast<const U*>(_ptr);
#ifdef CSMARTPTR_CAST_THROWS
    if(!ptr) throw std::bad_cast();
#endif
    return SmartPtr<const U>(ptr,_refCounter);
}

template <typename T>
bool SmartPtr<const T>::isShared() const {
    if(_refCounter)
        return *_refCounter>1;
    return false;
}

template <typename T>
bool SmartPtr<const T>::isNull() const {
    return !_refCounter;
}

template <typename T>
void SmartPtr<const T>::detach() {
    if(isShared()) {
        release();
        _refCounter = new int(1);
        const T* _tmp = _ptr;
        _ptr = new T(*_tmp);
    }
}

template <typename T>
void SmartPtr<const T>::retain() const {
    if(_refCounter){
        ++*_refCounter;
    }
}

template <typename T>
void SmartPtr<const T>::release() const {
    if(_refCounter){
        --*_refCounter;
        if(!*_refCounter)
        {
            delete _refCounter;
            delete _ptr;
            _refCounter = 0;
        }
    }
}

template <typename T>
bool SmartPtr<const T>::operator==(const SmartPtr& p) const {
    return _ptr==p._ptr;
}

template <typename T>
bool SmartPtr<const T>::operator!=(const SmartPtr& p) const {
    return _ptr!=p._ptr;
}

/*
    IMPLEMENTARE SmartPtr<T>
*/

template <typename T>
SmartPtr<T>::SmartPtr(const T* ptr, int* counter):_ptr(const_cast<T*>(ptr)),_refCounter(counter){
    if(_ptr){
        if(!_refCounter)
            _refCounter = new int(0);
        retain();
    }
    else {
        _refCounter = 0;
    }
}

template <typename T>
SmartPtr<T>::SmartPtr(const SmartPtr& rhs):_ptr(rhs._ptr),_refCounter(rhs._refCounter){
    retain();
}

template <typename T>
SmartPtr<T>::~SmartPtr(){
    release();
}

template <typename T>
SmartPtr<T>& SmartPtr<T>::operator=(const SmartPtr& rhs){
    rhs.retain();
    release();
    _refCounter = rhs._refCounter;
    _ptr = rhs._ptr;
    return *this;
}

template <typename T>
T* SmartPtr<T>::operator->() const{
    return _ptr;
}

template <typename T>
T& SmartPtr<T>::operator*() const{
    return *_ptr;
}


template <typename T>
template <typename U>
SmartPtr<T>::operator SmartPtr<U>() const
#ifdef CSMARTPTR_CAST_THROWS
throw(std::bad_cast)
#endif
{
    const U* ptr = dynamic_cast<U*>(_ptr);
#ifdef CSMARTPTR_CAST_THROWS
    if(!ptr) throw std::bad_cast();
#endif
    return SmartPtr<U>(ptr,_refCounter);
}

template <typename T>
bool SmartPtr<T>::isShared() const {
    if(_refCounter)
        return *_refCounter>1;
    return false;
}

template <typename T>
bool SmartPtr<T>::isNull() const {
    return !_refCounter;
}

template <typename T>
void SmartPtr<T>::detach() {
    if(isShared()) {
        release();
        _refCounter = new int(1);
        T* _tmp = _ptr;
        _ptr = new T(*_tmp);
    }
}

template <typename T>
void SmartPtr<T>::retain() const {
    if(_refCounter){
        ++*_refCounter;
    }
}

template <typename T>
void SmartPtr<T>::release() const {
    if(_refCounter){
        --*_refCounter;
        if(!*_refCounter)
        {
            delete _refCounter;
            delete _ptr;
            _refCounter = 0;
        }
    }
}

template <typename T>
bool SmartPtr<T>::operator==(const SmartPtr& p) const {
    return _ptr==p._ptr;
}

template <typename T>
bool SmartPtr<T>::operator!=(const SmartPtr& p) const {
    return _ptr!=p._ptr;
}

template <typename T1, typename T2>
bool operator==(const SmartPtr<T1>& p1,const SmartPtr<T2>& p2) {
    return ((SmartPtr<const typename RemoveConst<T1>::Type>)p1).operator==((SmartPtr<const typename RemoveConst<T1>::Type>)p2);
}

template <typename T1, typename T2>
bool operator!=(const SmartPtr<T1>& p1,const SmartPtr<T2>& p2) {
    return !(p1==p2);
}

#endif // SMARTPTR_H
